import { app,ipcMain,dialog } from 'electron'
const fs = require('fs')
const crypto = require('crypto')
const compressing = require('compressing')

//用户数据存储路径
const savePath=app.getPath('userData')+'/save'
//配置文件路径
const configPath=savePath+'/config.json'
//判断是否存在save存档路径
if(!fs.existsSync(savePath)){
  fs.mkdirSync(savePath)
  fs.writeFileSync(configPath,'{"version":1,"lastid":0,"dirs":[]}')
}
//定义数据
let data={} as any
data['config']=JSON.parse(fs.readFileSync(configPath))//读取配置文件数据
data['selDirIdx']=null//当前选择的目录

//导出配置
ipcMain.handle('/export/config', (event,arg) => {
  return dialog.showOpenDialog({properties: ['openDirectory']}).then(o=>{
    let path=o.filePaths[0]+'/gitfilelist-config.zip'
    return compressing.zip.compressDir(savePath,path).then(()=>{
      return path.replace('/','\\')
    })
  })
})
//导入配置
ipcMain.handle('/import/config', (event,arg) => {
  return savePath
})

// #region 目录相关
  //设置默认
  ipcMain.handle('/dir/default/set', (event,arg) => {
    arg=JSON.parse(arg)
    
    for(let i=0;i<data.config.dirs.length;i++){
      if(data.config.dirs[i].id==arg.id){//设置默认
        data.config.dirs[i].default=true
      }else{
        //判断和取消原有默认
        if(data.config.dirs[i].default)data.config.dirs[i].default=false
      }
    }

    //写入配置
    writeConfig()

    return data.config.dirs
  })
  //添加排除列表
  ipcMain.handle('/dir/exclude/add', (event,arg) => {
    arg=JSON.parse(arg)

    for(let i=0;i<data.config.dirs.length;i++){
      if(data.config.dirs[i].id==arg.id){
        let split=arg.content.split('\n')
        data.config.dirs[i].exclude=split.filter(v=>v!='')
        break
      }
    }

    //写入配置文件
    writeConfig()

    return data.config.dirs
  })
  //获取目录列表
  ipcMain.handle('/dir/list', (event,arg) => {return data.config.dirs})
  //添加目录
  ipcMain.handle('/dir/add', (event,arg) => {
    arg=JSON.parse(arg)

    //添加参数
    data.config.lastid++
    arg.id=data.config.lastid
    arg.default=false
    arg.exclude=[]
    arg.list=[]

    //根据id添加文件
    fs.writeFileSync(savePath+'/'+arg.id+'.json','{}')

    //添加目录
    data.config.dirs.push(arg)

    //写入配置文件
    writeConfig()

    return data.config.dirs
  })
  //移除目录
  ipcMain.handle('/dir/del', (event,arg) => {
    arg=JSON.parse(arg)

    for(let i=0;i<data.config.dirs.length;i++){
      if(data.config.dirs[i].id==arg.id){
        data.config.dirs.splice(i,1)
        break
      }
    }

    //根据id删除文件
    fs.unlinkSync(savePath+'/'+arg.id+'.json')

    //写入配置文件
    writeConfig()

    return data.config.dirs
  })
  //选择目录
  ipcMain.handle('/dir/select', (event,arg) => {
    return dialog.showOpenDialog({properties: ['openDirectory']})
  })
// #endregion

// #region 版本相关
  //版本对比
  ipcMain.handle('/version/compare', (event,arg) => {
    arg=JSON.parse(arg)

    //读取版本json
    let json = readVersion()

    let oldVer=arg.old=='now'?listDirSync(data.config.dirs[data.selDirIdx].path,data.config.dirs[data.selDirIdx].exclude):json[arg.old]
    let newVer=arg.new=='now'?listDirSync(data.config.dirs[data.selDirIdx].path,data.config.dirs[data.selDirIdx].exclude):json[arg.new]

    //获取新增和移除的键
    let oldKeys=Object.keys(oldVer)
    let newKeys=Object.keys(newVer)
    let addKeys=newKeys.filter(v=>!oldKeys.includes(v))
    let delKeys=oldKeys.filter(v=>!newKeys.includes(v))

    //获取新增和移除的文件集合
    let addFiles=addKeys.map(v=>newVer[v].name)
    let delFiles=delKeys.map(v=>oldVer[v].name)

    //获取更新的文件集合
    let updateFiles=addFiles.filter(v=>delFiles.includes(v))

    //新增和移除的文件集合中移除更新的
    addFiles=addFiles.filter(v=>!updateFiles.includes(v))
    delFiles=delFiles.filter(v=>!updateFiles.includes(v))

    //合并数组
    let allFiles=[]
    for(let v of addFiles){allFiles.push({type:'add',path:v})}
    for(let v of updateFiles){allFiles.push({type:'update',path:v})}
    for(let v of delFiles){allFiles.push({type:'del',path:v})}
    //树形结构
    let tree=buildTree(allFiles)
    let eltree=treeToArray(tree)

    return {tree:eltree,compare:{add:addFiles,del:delFiles,update:updateFiles}}
  })
  //修改版本的描述
  ipcMain.handle('/version/desc/edit', (event,arg) => {
    arg=JSON.parse(arg)
    let idx=data.selDirIdx
    
    //修改配置文件
    for(let i=0;i<data.config.dirs[idx].list.length;i++){
      if(data.config.dirs[idx].list[i].time==arg.time){
        data.config.dirs[idx].list[i].desc=arg.desc
        break
      }
    }

    //写入
    writeConfig()

    return data.config.dirs[idx].list
  })
  //获取指定版本的文件列表
  ipcMain.handle('/version/list/get', (event,arg) => {
    arg=JSON.parse(arg)

    //读取版本json
    let json = readVersion()

    return json[arg.time]
  })
  //切换目录并获取版本
  ipcMain.handle('/version/list', (event,arg) => {
    arg=JSON.parse(arg)

    for(let i=0;i<data.config.dirs.length;i++){
      if(data.config.dirs[i].id==arg.id){
        data.selDirIdx=i
        return data.config.dirs[i].list
      }
    }
  })
  //删除版本
  ipcMain.handle('/version/del', (event,arg) => {
    arg=JSON.parse(arg)
    let idx=data.selDirIdx
    let id=data.config.dirs[idx].id
    
    //读取版本json
    let json = readVersion()

    //删除版本列表
    delete json[arg.time]
    fs.writeFileSync(savePath+'/'+id+'.json',JSON.stringify(json))

    //修改config
    let delIdx=null
    for(let i=0;i<data.config.dirs[idx].list.length;i++){
      if(data.config.dirs[idx].list[i].time==arg.time){
        delIdx=i
        break
      }
    }
    data.config.dirs[idx].list.splice(delIdx,1)

    //写入配置文件
    writeConfig()

    return data.config.dirs[idx].list
  })
  //添加版本
  ipcMain.handle('/version/add', (event,arg) => {
    arg=JSON.parse(arg)
    let i=data.selDirIdx
    let id=data.config.dirs[i].id

    let timestamp = new Date().getTime();
    let time = Number(timestamp.toString().slice(0,10))

    //读取版本json
    let json = readVersion()

    //获取目录下的文件列表
    let files=listDirSync(data.config.dirs[i].path,data.config.dirs[i].exclude)

    //添加到版本json中
    json[time.toString()]=files
    fs.writeFileSync(savePath+'/'+id+'.json',JSON.stringify(json))

    //添加到config中
    data.config.dirs[i].list.push({
      time,
      desc:arg.desc
    })
    writeConfig()

    return data.config.dirs[i].list
  })
// #endregion

// #region 方法
  //树形结构children对象转数组
  function treeToArray(tree) {
    const result = [];
    for (const key in tree) {
      const node = tree[key];
      const children = treeToArray(node.children); // 递归转换children属性
      let tmp={
        label: node.label,
        children,
      } as any
      if(node.type)tmp.type=node.type
      result.push(tmp);
    }
    return result;
  }
  //路径数组转树形结构
  function buildTree(arr) {
    const tree = {};
    arr.forEach(v => {
      const dirs = v.path.split('/');
      let node = tree;
      let label = '';
      dirs.forEach((dir,i) => {
        if (!node[dir]) {
          label = dir;
          node[dir] = { label, children: {} };
          if(i==dirs.length-1)node[dir].type=v.type
        }
        node = node[dir].children;
      });
    });
    return tree;
  }
  //读取版本json
  function readVersion(){
    let id=data.config.dirs[data.selDirIdx].id
    let str = fs.readFileSync(savePath+'/'+id+'.json')
    return JSON.parse(str)
  }
  //写入当前配置
  function writeConfig(){
    fs.writeFileSync(configPath,JSON.stringify(data.config))
  }
  //遍历获取目录下所有文件信息
  function listDirSync(dirPath,exclude){
    let obj = {}
    if(dirPath.substr(-1)=='\\')dirPath=dirPath.substr(0,dirPath.length-1)
    let subLen=dirPath.length

    let callback=(path)=>{
      var pa = fs.readdirSync(path)
      for(const file of pa){
        //排除特殊文件和目录
        let exp = ['System Volume Information','$RECYCLE.BIN']
        if(exp.indexOf(file)!=-1)continue

        //排除隐藏文件
        //if(file.startsWith('.'))continue

        //文件路径
        let filePath=path+"/"+file
        let partPath=filePath.slice(subLen+1)

        //排除自定义
        let mat=false
        for(let v of exclude){
          if(v.indexOf('*')==-1){//如果不包含*
            if(partPath.startsWith(v)){
              mat=true
              break
            }
          }else{//有*号要处理
            if(patPath(v,partPath)){
              mat=true
              break
            }
          }
        }
        if(mat)continue
        
        var info = fs.statSync(filePath)
        if(info.isDirectory()){
          callback(filePath)
        }else{
          //处理文件
          let name=filePath
          name=name.slice(subLen)
          let time=GMTToStr(info.mtime)
          let size=info.size
          let key=md5(name+time+size)
          obj[key]={name,time,size}
        }
      }
    }

    callback(dirPath)
    return obj
  }
  //处理带*号的正则匹配路径
  function patPath(patt:string,path:string){
    //处理字符
    let cha='$()+.[]^{}'
    for(let v of cha){
      patt=patt.replace(v,'\\'+v)
    }
    patt='^'+patt+'$'
    patt=patt.replace('*','.*?')
    const reg = new RegExp(patt)

    //匹配
    return reg.test(path)
  }
  //md5加密
  function md5(content) {
    const _md5 = crypto.createHash('md5')
    return _md5.update(content).digest('hex')
  }
  //时间格式化
  function GMTToStr(time){
    let date = new Date(time)
    let year = date.getFullYear()
    let month = date.getMonth()+1<10?'0'+(date.getMonth()+1):(date.getMonth()+1)
    let day = date.getDate()<10?'0'+date.getDate():date.getDate()
    let str = year + '-' + month + '-' + day
    let hours = date.getHours()<10?'0'+date.getHours():date.getHours()
    let minutes = date.getMinutes()<10?'0'+date.getMinutes():date.getMinutes();
    let seconds = date.getSeconds()<10?'0'+date.getSeconds():date.getSeconds();
    str += ' ' + hours + ':' + minutes + ':' + seconds
    return str
  }
// #endregion